#ifndef TREE_H
#define TREE_H

#include <memory>
#include <vector>
#include "TrainingData.h"

#define LEFT_CHILD 0
#define RIGHT_CHILD 1

template <typename _T> class TreeNode {
	
private:

	std::vector<unsigned int> _trainingRecordIndexes;
	unsigned int _splitFeature;
	_T _threshold;
	
	bool _isLeaf;

	std::auto_ptr<TreeNode<_T>> _autoPtrLeftNode;
	std::auto_ptr<TreeNode<_T>> _autoPtrRightNode;

	/* Costruttore di copia privato. Evita la copia	di un oggetto TreeNode (che in questo caso sarebbe
	comunque possibile facendo uso di auto_ptr) */
	TreeNode(const TreeNode &tn);

public:

	TreeNode();
	~TreeNode();
	
	void pushTrainingRecordIndex(unsigned int recordIndex);
	void trainingRecordIndexAt(unsigned int index);

	unsigned int getVectorTrainingRecordIndexesSize();
	
	void setIsLeaf(bool isLeaf);
	bool isLeaf();

	
	void setSplitFeature(unsigned int splitFeature);
	unsigned int getSplitFeature();

	void setThreshold(_T threshold);
	_T getThreshold();

	
	/* auto_ptr can be passed as copy only if its pointer is kept
	inside the method that receives the auto_ptr copy. As a matter of 
	fact, one the auto_ptr object is copied, the original one loses 
	the ownership of the pointer */
	void setLeftChild(std::auto_ptr<TreeNode<_T>> autoPtrNode);
	void setRightChild(std::auto_ptr<TreeNode<_T>> autoPtrNode);
		
	/* Ritorna una reference all'oggetto auto_ptr che rappresenta
	il nodo di sinistra. La reference evita di far perdere a 
	TreeNode l'ownership del puntatore */
	std::auto_ptr<TreeNode<_T>>& getLeftChild();

	/* Ritorna una reference all'oggetto auto_ptr che rappresenta
	il nodo di destra. La reference evita di far perdere a 
	TreeNode l'ownership del puntatore */
	std::auto_ptr<TreeNode<_T>>& getRightChild();
	
	/* Ritorna una reference al vettore degli indici dei record di 
	training. Viene usato per popolare il vettore senza 
	alcun passaggio per copia */
	std::vector<unsigned int>& getVectorTrainingRecordIndexes();


	/* Filtra i dati appartenenti al nodo in base ad una
	feature e ad un determinato valore di threshold. Inserisce
	gli indici dei record di training filtrati all'interno del 
	vettore passato come reference */
	void getFilteredRecords(unsigned int feature,
							_T threshold,
							unsigned char LEFT_RIGHT_CHILD,
							TrainingData<_T> &trainingData,
							std::vector<unsigned int>& outputFilteredRecords);

};


template <typename _T> class RandomTree {

private:

	unsigned int _inputSpaceDimension;
	std::auto_ptr<TreeNode<_T>> _ptrRoot;
	
	void split(	std::auto_ptr<TreeNode<_T>> &parent, 
				TrainingData<_T> &trainingData, 
				int height, 
				unsigned char algorithm);

	/* Dataset di training interno usato con il bagging. Viene costruito a random a partire
	dal dataset globale */
	TrainingData<_T> _trainingData;

public:
	void setInputSpaceDimension(unsigned int inputSpaceDimension);
	unsigned int getInputSpaceDimension();

	RandomTree();
	~RandomTree();
	
	void trainTree(TrainingData<_T> &trainingData, int height, unsigned char algorithm);
	
	void setTrainingData(TrainingData<_T> trainingData);
	unsigned int classify(TrainingData<_T> &trainingData, Record<_T> &record, bool useInternalDataset);


};
#endif
